﻿using System;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml.Schema;
using Automator.Runtime.Classes;

namespace Automator.Runtime
{
    public sealed class Solution: RuntimeEntity
    {
        #region Fields

        private WorkflowCollection _workflows = new WorkflowCollection();

        #endregion

        #region Properties

        /// <summary>
        /// Solution name
        /// </summary>
        public override string Name
        {
            get
            {
                StringBuilder sb = new StringBuilder();
                if (!this.Id.Equals(Guid.Empty))
                    sb.Append(this.FileName);
                else
                {
                    if (String.IsNullOrEmpty(FileName))
                        sb.Append("New Solution");
                    else sb.Append(Path.GetFileName(FileName));
                }
                if (this.Modified)
                    sb.Append("*");
                return sb.ToString();
            }
        }

        public ReadOnlyWorkflowCollection Workflows
        {
            get { return new ReadOnlyWorkflowCollection(_workflows); }
        }

        #endregion

        #region Methods
        
        public Solution():
            base()
        {
            if (XmlValidation.SolutionSchema != null)
                this.XSDSchema = XmlValidation.SolutionSchema;
            else
                this.Log.AddLog(LogMessageType.Warning, "Could not load Solution XSD schema");
            this.FileFormatCommentString = "Automator Solution File, Format Version ";
        }

        public Workflow CreateWorkflow()
        {
            Workflow result = new Workflow();
            AddWorkflow(result);
            return result;
        }
        
        public void AddWorkflow(Workflow value)
        {
            value.Parent = this;
            this._workflows.Add(value);
            this.Modified = true;            
        }

        public bool RemoveWorkflow(Workflow value)
        {
            this.Modified = true;
            if (this._workflows.Remove(value))
            {
                value.Parent = null;
                return true;
            }
            else
                return false;
            
        }

        public void ClearWorkflows()
        {
            this._workflows.Clear();
        }

        /// <summary>
        /// Serializes a solution into XML
        /// </summary>
        /// <returns>XDocument with serialized solution</returns>
        public override XDocument Serialize()
        {
            this.XML = new XDocument(
                new XDeclaration("1.0", "utf-8", "yes"),
                new XComment(String.Format("{0}{1}", this.FileFormatCommentString, XmlValidation.CurrentFormatVersion)),
                new XElement(XmlValidation.SolutionNamespace + "Solution",
                             new XAttribute("Id", this.Id.ToString()),
                             new XElement(XmlValidation.SolutionNamespace + "Workflows", Workflows.Where(t => !String.IsNullOrEmpty(t.FileName)).
                                          Select(t => new XElement(XmlValidation.SolutionNamespace + "Workflow",
                                                                   new XAttribute("Id", t.Id.ToString()),
                                                                   new XAttribute("Uri", new Uri(t.FileName, UriKind.RelativeOrAbsolute))))),
                             new XElement(XmlValidation.SolutionNamespace + "GlobalVariables")
                            )
               );
            
            return this.XML;
        }

        /// <summary>
        /// Loads solution from a file
        /// </summary>
        protected override void Deserialize()
        {
            base.Deserialize();
            try
            {
                foreach (XElement element in this.XML.Element(XmlValidation.SolutionNamespace + "Solution").Element(XmlValidation.SolutionNamespace + "Workflows").Elements())
                {
                    try
                    {
                        if (element.Name.LocalName.Equals("Workflow"))
                        {
                            Guid guid = Guid.Parse(element.Attribute("Id").Value);
                            if (!guid.Equals(Guid.Empty))
                            {
                                DataTable dt = DataAccess.Select(guid);
                                if (dt != null && dt.Rows.Count > 0)
                                {
                                    DataRowView dv = dt.DefaultView[0];
                                    Workflow workflow = this.CreateWorkflow();
                                    workflow.Load(
                                        Guid.Parse(dv["Id"].ToString()), 
                                        dv["Name"].ToString(), 
                                        dv["XmlData"].ToString());
                                    workflow.Modified = false;
                                }
                            }
                            else
                            {
                                Uri workflowUri = new Uri(element.Attribute("Uri").Value);
                                if (File.Exists(workflowUri.LocalPath))
                                {
                                    Workflow workflow = this.CreateWorkflow();
                                    workflow.Load(workflowUri.LocalPath);
                                    workflow.Modified = false;
                                }
                                else
                                    throw new FileNotFoundException(String.Format("File not found: {0}", workflowUri.LocalPath));
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        this.Log.AddLog(LogMessageType.Error, ex.Message);
                    }
                }
            }
            catch (Exception ex)
            {
                this.Log.AddLog(LogMessageType.Error, ex.Message);
            }
        }
        
        #endregion
    }
}
